﻿using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Loong.Domain.Uow;
using Loong.Transactions.Extensions;

namespace Loong.Dapper.Uow
{
    public class DapperUnitOfWork : UnitOfWorkBase
    {
        private readonly SemaphoreSlim _locker;
        private readonly IDbConnectionProvider _dbConnectionProvider;

        protected ActiveTransactionInfo ActiveTransaction { get; private set; }

        public DapperUnitOfWork(
            IUnitOfWorkDefaultOptions defaultOptions,
            IUnitOfWorkFilterExecuter filterExecuter,
            IDbConnectionProvider dbConnectionProvider)
            : base(defaultOptions, filterExecuter)
        {
            _locker = new SemaphoreSlim(1);
            _dbConnectionProvider = dbConnectionProvider;
        }

        public override void SaveChanges()
        {
        }

        public override Task SaveChangesAsync()
        {
            return Task.CompletedTask;
        }

        protected override void CompleteUow()
        {
            SaveChanges();
            CommitTransaction();
        }

        private void CommitTransaction()
        {
            if (Options.IsTransactional == true &&
                ActiveTransaction != null)
            {
                ActiveTransaction.Commit();
            }
        }

        protected override async Task CompleteUowAsync()
        {
            await SaveChangesAsync();
            await CommitTransactionAsync();
        }

        private async Task CommitTransactionAsync()
        {
            if (Options.IsTransactional == true &&
                ActiveTransaction != null)
            {
                await ActiveTransaction.CommitAsync();
            }
        }

        protected override void DisposeUow()
        {
            if (ActiveTransaction != null)
            {
                ActiveTransaction.Dispose();
                ActiveTransaction = null;
            }
        }

        public virtual ActiveTransactionInfo GetOrCreateTransaction()
        {
            try
            {
                _locker.Wait();

                if (ActiveTransaction == null)
                {
                    var connection = _dbConnectionProvider.GetDbConnection();
                    if (connection == null)
                    {
                        throw new LoongException("打开数据库连接失败");
                    }

                    if (connection.State != ConnectionState.Open)
                    {
                        connection.Open();
                    }

                    DbTransaction transaction = null;
                    if (Options.IsTransactional == true)
                    {
                        var il = (Options.IsolationLevel ?? System.Transactions.IsolationLevel.ReadCommitted).ToSystemDataIsolationLevel();
                        transaction = connection.BeginTransaction(il);
                    }

                    ActiveTransaction = new ActiveTransactionInfo(connection, transaction);
                }

                return ActiveTransaction;
            }
            finally
            {
                _locker.Release();
            }
        }
    }
}
